﻿#pragma once
#include <fmt/color.h>
#include <fmt/ostream.h>
#include <fmt/ranges.h>
#include <spdlog/spdlog.h>
#include <stdio.h>

#include <iostream>
#include <string_view>

/// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx print separators
// print newline
#define MY_C_PRT_NEWLINE printf("\n")
/// basic sep
inline void MySep() { std::cout << "======================"; }
inline void MySubSep() { std::cout << "--------------"; }
/// sep with newline
inline void MySepln() { printf("==================\n"); }
inline void MySubSepln() { printf("--------------\n"); }

/// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx format print
inline void MyMessage(std::string msg) { std::cout << "---- " << msg << "\n"; }

template <typename... Args>
auto MyPrint(Args&&... args) -> decltype(::fmt::print(std::forward<Args>(args)...))
{
    return fmt::print(std::forward<Args>(args)...);
}

/// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx print colors
/// raw console control sequences
#if 0
#define MY_CLR_FG_BL (::std::cout << "\033[0m\033[1;36m");
#define MY_CLR_FG_BL (printf("\033[0m\033[1;36m"))
#define MY_CLR_FG_BL (printf("\033[0m\033[38;2;0;255;255m"))
#define MY_CLR_FG_RESET (printf("\033[0m"))
#endif

///
#define MY_CLR_FG_BL_STRING "\033[0m\033[38;2;0;255;255m"
#define MY_CLR_FG_RESET_STRING "\033[0m"
#define MY_CLR_FG_BL (::std::cout << MY_CLR_FG_BL_STRING)
#define MY_CLR_FG_RESET (::std::cout << MY_CLR_FG_RESET_STRING)

//#define MY_FLUSH_STD_OUT_ std::fflush(stdout)
#define MY_FLUSH_STD_OUT_

/// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Logger macros
#define MYINFO(...) ::spdlog::info(__VA_ARGS__)
#define MYDEBUG(...) ::spdlog::debug(__VA_ARGS__)
#define MYWARN(...) ::spdlog::warn(__VA_ARGS__)
#define MYERROR(...) ::spdlog::error(__VA_ARGS__)

///
#define MY_SUBSEP                       \
    spdlog::info("------------------"); \
    MY_FLUSH_STD_OUT_;

#define MY_SEP_TEST                                       \
    spdlog::info("\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); \
    MY_FLUSH_STD_OUT_;

#define MY_SEP                             \
    spdlog::info("====================="); \
    MY_FLUSH_STD_OUT_;

#define MY_NEWLINE    \
    spdlog::info(""); \
    MY_FLUSH_STD_OUT_;

/// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx print function path, variables
#define MY_ECHOFUNPATH                                                               \
    /* printf("Name: %s\n", __FUNCTION__); */                                        \
    /*::std::cout << "Name: " << __FUNCTION__ << " -------------" << ::std::endl; */ \
    spdlog::info("============================ Name: {}", __FUNCTION__);             \
    MY_FLUSH_STD_OUT_;

/// print variable
#define MY_ECHO(V) ::std::cout << #V " = " << V << "\n";
#define MY_ECHO2(M, V) ::std::cout << M " = " << V << "\n";

/// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx print raw array
template <typename T>
void MyPrtArr(const T* v, size_t cnt)
{
    if (cnt == 0)
    {
        MyPrint("[ ]");
        return;
    }
    MyPrint("[ ");
    for (size_t i = 0; i < cnt - 1; i++) MyFmtPrt("{}, ", v[i]);
    MyFmtPrt("{} ]", v[cnt - 1]);
}

//------------ print raw array with newline
template <typename T>
void MyPrtArrLn(const T* v, size_t cnt)
{
    MyPrtArr(v, cnt);
    MY_C_PRT_NEWLINE;
}

/// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx print ranges

/// iterators
template <typename T>
void MyPrtIters(::std::string_view name, T const& q)
{
    ::std::cout << name << ": \t";
    for (auto const& n : q) ::std::cout << n << ' ';
    MY_C_PRT_NEWLINE;
}

/// print std::queue
template <typename Q>
void MyPrtQueue(std::string_view name, Q q)
{
    // 注意：按值传递 q，这是因为无法在不清楚队列的情况下遍历 priority_queue 的内容。
    for (::std::cout << name << ":" << ::std::endl; !q.empty(); q.pop())
    {
        ::std::cout << q.top() << ' ';
    }
    MY_C_PRT_NEWLINE;
}

/// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx eigen format
#define MyEigenFormat Eigen::IOFormat _myEigenFmt(Eigen::StreamPrecision, 0, ", ", ";\n", "", "", "[", "]");

/// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 打印表达式的类型信息
template <typename T>
::std::string TypeToString()
{
#if defined(_MSC_VER)
    ::std::string type_name{__FUNCSIG__};
    // class ::std:: basic_string<char,struct ::std:: char_traits<char>,class ::std:: allocator<char> >
    //    __cdecl type_to_string<int&>(void)
    auto start_pos = type_name.find_first_of('<', ::std::string(typeid(::std::string).name()).size()) + 1;
    auto end_pos = type_name.find_last_of('>');
    return type_name.substr(start_pos, (end_pos - start_pos));
#elif defined(__clang__)
    ::std::string type_name{__PRETTY_FUNCTION__};
    auto start_pos = type_name.find_first_of('=') + 2;
    auto end_pos = type_name.find_first_of(']', start_pos);
    return type_name.substr(start_pos, (end_pos - start_pos));

#elif defined(__GNUC__)
    ::std::string type_name{__PRETTY_FUNCTION__};
    // ::std:: __cxx11::string type_to_string() [with T = int&; ::std:: __cxx11::string = ::std:: __cxx11::basic_string<char>]
    auto start_pos = type_name.find_first_of('=') + 2;
    auto end_pos = type_name.find_first_of(';', start_pos);
    return type_name.substr(start_pos, (end_pos - start_pos));
#endif
}
